1   /*
2    * Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package javax.swing.plaf.metal;
27  
28  import java.awt.*;
29  import javax.swing.*;
30  import javax.swing.border.*;
31  import javax.swing.plaf.basic.*;
32  
33  
34  /**
35   * Metal's split pane divider
36   * <p>
37   * <strong>Warning:</strong>
38   * Serialized objects of this class will not be compatible with
39   * future Swing releases. The current serialization support is
40   * appropriate for short term storage or RMI between applications running
41   * the same version of Swing.  As of 1.4, support for long term storage
42   * of all JavaBeans<sup><font size="-2">TM</font></sup>
43   * has been added to the <code>java.beans</code> package.
44   * Please see {@link java.beans.XMLEncoder}.
45   *
46   * @author Steve Wilson
47   * @author Ralph kar
48   */
49  class MetalSplitPaneDivider extends BasicSplitPaneDivider
50  {
51      private MetalBumps bumps = new MetalBumps(10, 10,
52                   MetalLookAndFeel.getControlHighlight(),
53                   MetalLookAndFeel.getControlDarkShadow(),
54                   MetalLookAndFeel.getControl() );
55  
56      private MetalBumps focusBumps = new MetalBumps(10, 10,
57                   MetalLookAndFeel.getPrimaryControlHighlight(),
58                   MetalLookAndFeel.getPrimaryControlDarkShadow(),
59                   UIManager.getColor("SplitPane.dividerFocusColor"));
60  
61      private int inset = 2;
62  
63      private Color controlColor = MetalLookAndFeel.getControl();
64      private Color primaryControlColor = UIManager.getColor(
65                                  "SplitPane.dividerFocusColor");
66  
67      public MetalSplitPaneDivider(BasicSplitPaneUI ui) {
68          super(ui);
69      }
70  
71      public void paint(Graphics g) {
72          MetalBumps usedBumps;
73          if (splitPane.hasFocus()) {
74              usedBumps = focusBumps;
75              g.setColor(primaryControlColor);
76          }
77          else {
78              usedBumps = bumps;
79              g.setColor(controlColor);
80          }
81          Rectangle clip = g.getClipBounds();
82          Insets insets = getInsets();
83          g.fillRect(clip.x, clip.y, clip.width, clip.height);
84          Dimension  size = getSize();
85          size.width -= inset * 2;
86          size.height -= inset * 2;
87          int drawX = inset;
88          int drawY = inset;
89          if (insets != null) {
90              size.width -= (insets.left + insets.right);
91              size.height -= (insets.top + insets.bottom);
92              drawX += insets.left;
93              drawY += insets.top;
94          }
95          usedBumps.setBumpArea(size);
96          usedBumps.paintIcon(this, g, drawX, drawY);
97          super.paint(g);
98      }
99  
100     /**
101      * Creates and return an instance of JButton that can be used to
102      * collapse the left component in the metal split pane.
103      */
104     protected JButton createLeftOneTouchButton() {
105         JButton b = new JButton() {
106             // Sprite buffer for the arrow image of the left button
107             int[][]     buffer = {{0, 0, 0, 2, 2, 0, 0, 0, 0},
108                                   {0, 0, 2, 1, 1, 1, 0, 0, 0},
109                                   {0, 2, 1, 1, 1, 1, 1, 0, 0},
110                                   {2, 1, 1, 1, 1, 1, 1, 1, 0},
111                                   {0, 3, 3, 3, 3, 3, 3, 3, 3}};
112 
113             public void setBorder(Border b) {
114             }
115 
116             public void paint(Graphics g) {
117                 JSplitPane splitPane = getSplitPaneFromSuper();
118                 if(splitPane != null) {
119                     int         oneTouchSize = getOneTouchSizeFromSuper();
120                     int         orientation = getOrientationFromSuper();
121                     int         blockSize = Math.min(getDividerSize(),
122                                                      oneTouchSize);
123 
124                     // Initialize the color array
125                     Color[]     colors = {
126                             this.getBackground(),
127                             MetalLookAndFeel.getPrimaryControlDarkShadow(),
128                             MetalLookAndFeel.getPrimaryControlInfo(),
129                             MetalLookAndFeel.getPrimaryControlHighlight()};
130 
131                     // Fill the background first ...
132                     g.setColor(this.getBackground());
133                     if (isOpaque()) {
134                         g.fillRect(0, 0, this.getWidth(),
135                                    this.getHeight());
136                     }
137 
138                     // ... then draw the arrow.
139                     if (getModel().isPressed()) {
140                             // Adjust color mapping for pressed button state
141                             colors[1] = colors[2];
142                     }
143                     if(orientation == JSplitPane.VERTICAL_SPLIT) {
144                             // Draw the image for a vertical split
145                             for (int i=1; i<=buffer[0].length; i++) {
146                                     for (int j=1; j<blockSize; j++) {
147                                             if (buffer[j-1][i-1] == 0) {
148                                                     continue;
149                                             }
150                                             else {
151                                                 g.setColor(
152                                                     colors[buffer[j-1][i-1]]);
153                                             }
154                                             g.drawLine(i, j, i, j);
155                                     }
156                             }
157                     }
158                     else {
159                             // Draw the image for a horizontal split
160                             // by simply swaping the i and j axis.
161                             // Except the drawLine() call this code is
162                             // identical to the code block above. This was done
163                             // in order to remove the additional orientation
164                             // check for each pixel.
165                             for (int i=1; i<=buffer[0].length; i++) {
166                                     for (int j=1; j<blockSize; j++) {
167                                             if (buffer[j-1][i-1] == 0) {
168                                                     // Nothing needs
169                                                     // to be drawn
170                                                     continue;
171                                             }
172                                             else {
173                                                     // Set the color from the
174                                                     // color map
175                                                     g.setColor(
176                                                     colors[buffer[j-1][i-1]]);
177                                             }
178                                             // Draw a pixel
179                                             g.drawLine(j, i, j, i);
180                                     }
181                             }
182                     }
183                 }
184             }
185 
186             // Don't want the button to participate in focus traversable.
187             public boolean isFocusTraversable() {
188                 return false;
189             }
190         };
191         b.setRequestFocusEnabled(false);
192         b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
193         b.setFocusPainted(false);
194         b.setBorderPainted(false);
195         maybeMakeButtonOpaque(b);
196         return b;
197     }
198 
199     /**
200      * If necessary <code>c</code> is made opaque.
201      */
202     private void maybeMakeButtonOpaque(JComponent c) {
203         Object opaque = UIManager.get("SplitPane.oneTouchButtonsOpaque");
204         if (opaque != null) {
205             c.setOpaque(((Boolean)opaque).booleanValue());
206         }
207     }
208 
209     /**
210      * Creates and return an instance of JButton that can be used to
211      * collapse the right component in the metal split pane.
212      */
213     protected JButton createRightOneTouchButton() {
214         JButton b = new JButton() {
215             // Sprite buffer for the arrow image of the right button
216             int[][]     buffer = {{2, 2, 2, 2, 2, 2, 2, 2},
217                                   {0, 1, 1, 1, 1, 1, 1, 3},
218                                   {0, 0, 1, 1, 1, 1, 3, 0},
219                                   {0, 0, 0, 1, 1, 3, 0, 0},
220                                   {0, 0, 0, 0, 3, 0, 0, 0}};
221 
222             public void setBorder(Border border) {
223             }
224 
225             public void paint(Graphics g) {
226                 JSplitPane splitPane = getSplitPaneFromSuper();
227                 if(splitPane != null) {
228                     int         oneTouchSize = getOneTouchSizeFromSuper();
229                     int         orientation = getOrientationFromSuper();
230                     int         blockSize = Math.min(getDividerSize(),
231                                                      oneTouchSize);
232 
233                     // Initialize the color array
234                     Color[]     colors = {
235                             this.getBackground(),
236                             MetalLookAndFeel.getPrimaryControlDarkShadow(),
237                             MetalLookAndFeel.getPrimaryControlInfo(),
238                             MetalLookAndFeel.getPrimaryControlHighlight()};
239 
240                     // Fill the background first ...
241                     g.setColor(this.getBackground());
242                     if (isOpaque()) {
243                         g.fillRect(0, 0, this.getWidth(),
244                                    this.getHeight());
245                     }
246 
247                     // ... then draw the arrow.
248                     if (getModel().isPressed()) {
249                             // Adjust color mapping for pressed button state
250                             colors[1] = colors[2];
251                     }
252                     if(orientation == JSplitPane.VERTICAL_SPLIT) {
253                             // Draw the image for a vertical split
254                             for (int i=1; i<=buffer[0].length; i++) {
255                                     for (int j=1; j<blockSize; j++) {
256                                             if (buffer[j-1][i-1] == 0) {
257                                                     continue;
258                                             }
259                                             else {
260                                                 g.setColor(
261                                                     colors[buffer[j-1][i-1]]);
262                                             }
263                                             g.drawLine(i, j, i, j);
264                                     }
265                             }
266                     }
267                     else {
268                             // Draw the image for a horizontal split
269                             // by simply swaping the i and j axis.
270                             // Except the drawLine() call this code is
271                             // identical to the code block above. This was done
272                             // in order to remove the additional orientation
273                             // check for each pixel.
274                             for (int i=1; i<=buffer[0].length; i++) {
275                                     for (int j=1; j<blockSize; j++) {
276                                             if (buffer[j-1][i-1] == 0) {
277                                                     // Nothing needs
278                                                     // to be drawn
279                                                     continue;
280                                             }
281                                             else {
282                                                     // Set the color from the
283                                                     // color map
284                                                     g.setColor(
285                                                     colors[buffer[j-1][i-1]]);
286                                             }
287                                             // Draw a pixel
288                                             g.drawLine(j, i, j, i);
289                                     }
290                             }
291                     }
292                 }
293             }
294 
295             // Don't want the button to participate in focus traversable.
296             public boolean isFocusTraversable() {
297                 return false;
298             }
299         };
300         b.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
301         b.setFocusPainted(false);
302         b.setBorderPainted(false);
303         b.setRequestFocusEnabled(false);
304         maybeMakeButtonOpaque(b);
305         return b;
306     }
307 
308     /**
309      * Used to layout a MetalSplitPaneDivider. Layout for the divider
310      * involves appropriately moving the left/right buttons around.
311      * <p>
312      * This class should be treated as a &quot;protected&quot; inner class.
313      * Instantiate it only within subclasses of MetalSplitPaneDivider.
314      */
315     public class MetalDividerLayout implements LayoutManager {
316 
317         // NOTE NOTE NOTE NOTE NOTE
318         // This class is no longer used, the functionality has
319         // been rolled into BasicSplitPaneDivider.DividerLayout as a
320         // defaults property
321 
322         public void layoutContainer(Container c) {
323             JButton     leftButton = getLeftButtonFromSuper();
324             JButton     rightButton = getRightButtonFromSuper();
325             JSplitPane  splitPane = getSplitPaneFromSuper();
326             int         orientation = getOrientationFromSuper();
327             int         oneTouchSize = getOneTouchSizeFromSuper();
328             int         oneTouchOffset = getOneTouchOffsetFromSuper();
329             Insets      insets = getInsets();
330 
331             // This layout differs from the one used in BasicSplitPaneDivider.
332             // It does not center justify the oneTouchExpadable buttons.
333             // This was necessary in order to meet the spec of the Metal
334             // splitpane divider.
335             if (leftButton != null && rightButton != null &&
336                 c == MetalSplitPaneDivider.this) {
337                 if (splitPane.isOneTouchExpandable()) {
338                     if (orientation == JSplitPane.VERTICAL_SPLIT) {
339                         int extraY = (insets != null) ? insets.top : 0;
340                         int blockSize = getDividerSize();
341 
342                         if (insets != null) {
343                             blockSize -= (insets.top + insets.bottom);
344                         }
345                         blockSize = Math.min(blockSize, oneTouchSize);
346                         leftButton.setBounds(oneTouchOffset, extraY,
347                                              blockSize * 2, blockSize);
348                         rightButton.setBounds(oneTouchOffset +
349                                               oneTouchSize * 2, extraY,
350                                               blockSize * 2, blockSize);
351                     }
352                     else {
353                         int blockSize = getDividerSize();
354                         int extraX = (insets != null) ? insets.left : 0;
355 
356                         if (insets != null) {
357                             blockSize -= (insets.left + insets.right);
358                         }
359                         blockSize = Math.min(blockSize, oneTouchSize);
360                         leftButton.setBounds(extraX, oneTouchOffset,
361                                              blockSize, blockSize * 2);
362                         rightButton.setBounds(extraX, oneTouchOffset +
363                                               oneTouchSize * 2, blockSize,
364                                               blockSize * 2);
365                     }
366                 }
367                 else {
368                     leftButton.setBounds(-5, -5, 1, 1);
369                     rightButton.setBounds(-5, -5, 1, 1);
370                 }
371             }
372         }
373 
374         public Dimension minimumLayoutSize(Container c) {
375             return new Dimension(0,0);
376         }
377 
378         public Dimension preferredLayoutSize(Container c) {
379             return new Dimension(0, 0);
380         }
381 
382         public void removeLayoutComponent(Component c) {}
383 
384         public void addLayoutComponent(String string, Component c) {}
385     }
386 
387     /*
388      * The following methods only exist in order to be able to access protected
389      * members in the superclass, because these are otherwise not available
390      * in any inner class.
391      */
392 
393     int getOneTouchSizeFromSuper() {
394         return super.ONE_TOUCH_SIZE;
395     }
396 
397     int getOneTouchOffsetFromSuper() {
398         return super.ONE_TOUCH_OFFSET;
399     }
400 
401     int getOrientationFromSuper() {
402         return super.orientation;
403     }
404 
405     JSplitPane getSplitPaneFromSuper() {
406         return super.splitPane;
407     }
408 
409     JButton getLeftButtonFromSuper() {
410         return super.leftButton;
411     }
412 
413     JButton getRightButtonFromSuper() {
414         return super.rightButton;
415     }
416 }